use bytes::{BufMut, Bytes, BytesMut};

use super::Codec;

// const SIGN_BIT: u8 = 0x80;
// const NSEGS: u8 = 8;
const XLAW_QUANT_MASK: u8 = 0x0f;
const XLAW_SEG_MASK: u8 = 0x70;
const XLAW_SEG_SHIFT: u32 = 4;
fn search(val: i16) -> u8 {
	const SEG_END: [i16; 8] = [0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF];
	for i in 0..8 {
		if val <= SEG_END[i] {
			return i as u8;
		}
	}

	8
}

///将一个PCM的值转成ALAW pcm将存在正负值,并且将2个字节,13位...转成一个字节
pub(crate) fn linear_to_alaw(pcm_val: i16) -> u8 {
	let mut pcm_val = pcm_val;
	let mask: u8;
	let seg: u8;

	if pcm_val >= 0 {
		mask = 0xD5; /* sign (7th) bit = 1 */
	} else {
		mask = 0x55; /* sign bit = 0 */
		pcm_val = -pcm_val - 8;
	}

	/* Convert the scaled magnitude to segment number. */
	seg = search(pcm_val);

	/* Combine the sign, segment, and quantization bits. */
	let aval: u8 = match seg {
		0..=1 => (seg << XLAW_SEG_SHIFT) | ((pcm_val >> 4) as u8 & XLAW_QUANT_MASK),
		2..=7 => (seg << XLAW_SEG_SHIFT) | ((pcm_val >> (seg + 3)) as u8 & XLAW_QUANT_MASK),
		/* out of range, return maximum value. */
		_ => 0x7F,
	};

	aval ^ mask

	// 保留一下,原来的代码.
	// if seg >= 8 {
	
	// 	0x7F ^ mask
	// } else {
	// 	let mut aval = seg << XLAW_SEG_SHIFT;
	// 	if seg < 2 {
	// 		aval |= (pcm_val >> 4) as u8 & XLAW_QUANT_MASK;
	// 	} else {
	// 		aval |= (pcm_val >> (seg + 3)) as u8 & XLAW_QUANT_MASK;
	// 	}

	// 	aval ^ mask
	// }
}

fn alaw_to_linear(mut a_val: u8) -> i16 {
	a_val ^= 0x55;

	let mut t = i16::from((a_val & XLAW_QUANT_MASK) << 4);
	let seg = (a_val & XLAW_SEG_MASK) >> XLAW_SEG_SHIFT;

	match seg {
		0 => t += 0x8,
		1 => t += 0x108,
		_ => t = (t + 0x108) << (seg - 1),
	}

	if a_val & 0x80 == 0x80 {
		t
	} else {
		-t
	}
}

pub struct G711A;

impl Codec for G711A {
	fn decode_from_pcm(data: Bytes) -> Bytes {
		let mut buf = BytesMut::with_capacity(data.len() / 2);

		for i in 0..(data.len() / 2) {
			#[cfg(target_endian = "big")]
			let o: i16 = (data[i * 2] as i16) << 8 | (data[i * 2 + 1]) as i16;
			#[cfg(target_endian = "little")]
			let o: i16 = (data[i * 2 + 1] as i16) << 8 | (data[i * 2]) as i16;

			let r = linear_to_alaw(o);
			buf.put_u8(r);
		}

		buf.freeze()
	}

	fn encode_to_pcm(data: Bytes) -> Bytes {
		let mut buf = BytesMut::with_capacity(data.len() * 2);

		for i in 0..(data.len()) {
			let r = alaw_to_linear(data[i]);
			buf.put_i16(r);
		}

		buf.freeze()
	}
}
